﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.ComponentModel;
using System.Reflection;

namespace WebPALTT.Lib
{
    /// <summary>
    /// A Script Action containing the script code to run at runtime.
    /// </summary>
    [Serializable]
    public class ActionItem_Script : ScriptItem
    {
        /// <summary>
        /// Static method containing the CustomType of this action for comparison.
        /// </summary>
        public static string Type { get { return "C Sharp Script"; } }
        [CategoryAttribute("Action Properties"), ReadOnlyAttribute(true)]
        public override string CustomType { get { return ActionItem_Script.Type; } }        
        [CategoryAttribute("Action Properties"), ReadOnlyAttribute(true)]
        public override System.Drawing.Image Icon { get { return global::WebPALTT.Lib.Icons.page_white_csharp; } }
        
        public override bool HasChildren { get { return false; } }
        public override bool CanAddChildren { get { return false; } }

        public override string[] DataHeaders { get { return new string[] { "Return Value" }; } }
        public override string[] DataHeaders_Save { get { return new string[] { "Return Value" }; } }

        public override ExecutionStatus ExecutionStatus { get { return base.mExecutionStatus; } }

        string mMethod;
        string mCode;

        /// <summary>
        /// Gets or sets the name of the method created / run for this action.
        /// Changing this at runtime may cause errors.
        /// </summary>
        [DescriptionAttribute("The name of the method to call when running script.")]
        public string MethodName { get { return mMethod; } set { mMethod = value; } }

        [DescriptionAttribute("The name of the method to call when running script.")]
        public override string Name { get { return base.Name; } 
            set 
            { 
                base.Name = value;
                this.MethodName = value;
            } 
        }

        /// <summary>
        /// Gets or sets the code inside the method.
        /// Changes to this during runtime will not have any effect.
        /// </summary>
        [BrowsableAttribute(false)]
        public string Code { get { return mCode; } set { mCode = value; } }

        public override string VisibleCode { get { return this.mCode; } set { this.mCode = value; } }

        public override string ScriptCode
        {
            get
            {
                return "public string @" + MethodName + " (Scenario _Scenario, ScenarioResults _Results) {\r\n" +
                       mCode + "\r\n}\r\n";
            }
        }

        public override bool IsCodeEditable { get { return true; } }

        public ActionItem_Script() : this("") { }

        public ActionItem_Script(String name)
            : this(name, name, "return \"\";") { }

        public ActionItem_Script(String name, string startmethod, string code)
            : base(name)
        {
            startmethod.Replace(' ', '_');
            mMethod = startmethod;
            mCode = code;
        }

        public ActionItem_Script(SerializationInfo info, StreamingContext ctxt)
            : base(info, ctxt)
        {
            mMethod = (string)info.GetValue("Method", typeof(string));
            mCode = (string)info.GetValue("Code", typeof(string));
        }

        public override void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            base.GetObjectData(info, ctxt);
            info.AddValue("Method", mMethod);
            info.AddValue("Code", mCode);
        }

        public override ActionItem FindChild(Guid key) { return null; }
        public override ActionItem[] FindChildren(Guid key) { return null; }
        
        public override ActionItemCollection Children { get { return null; } }

        public override string[] Validate()
        {
            List<string> errors = new List<string> { };
            if (Name.Equals("Then") || Name.Equals("Else") || Name.Equals("Actions"))
                errors.Add("Error: action[" + Name + "(" + ActionType + ")] is using a reserverd name.");

            if (MethodName.IndexOfAny(new char[] { ' ', '!', '@', '#', '$', '%', '^', '&', '*', '\'', '\"', 
                '(', ')', '[', ']', '{', '}', ';', ':', '.', ',', '\\', '/' }) > -1)
                errors.Add("Error: action[" + Name + "(" + ActionType + ")].StartMethod contains invalid characters.");

            return errors.ToArray();
        }

        public override Dictionary<string, object> Execute(Scenario scenario, ScenarioResults resultScene, Type scriptType, object scriptObj)
        {
            Dictionary<string, object> results = new Dictionary<string, object>();
            base.mExecutionStatus = ExecutionStatus.Complete;
            MethodInfo _methodInfo = scriptType.GetMethod(this.MethodName);
            object returnval = _methodInfo.Invoke(scriptObj, new object[] { scenario, resultScene });
            if (returnval != null)
                results.Add("Return Value", returnval);
            return results;
        }
    }
}
